﻿using Mapster;
using Shop.Core.Extension;
using Shop.Core.Helper;
using Shop.Core.Repository;
using Shop.Core.Share;
using Shop.IServices.hea;
using Shop.Model.Dto.Hea;
using Shop.Model.Entity.Hea;
using Shop.Model.View.Hea;
using SqlSugar;

namespace Shop.Services.Hea;

public class HealthAppService: IHealthAppService
{
    private readonly RepositoryBase<HealthExam> _healthExamRepository;
    private readonly RepositoryBase<HealthAnswer> _healthAnswerRepository;
    private readonly RepositoryBase<HealthEvaluationLog> _healthEvaluationLogRepository;
    private readonly RepositoryBase<HealthEvaluationExamAnswer> _healthUserExamAnswerRepository;
    private readonly RepositoryBase<HealthAnswerDeduct> _healthAnswerDeductRepository;
    private readonly RepositoryBase<HealthAnswerTargetRate> _healthAnswerTargetRateRepository;
    private readonly RepositoryBase<HealthRiskResult> _healthRiskResultRepository;
    private readonly RepositoryBase<HealthEvaluationRsult> _healthEvaluationRsultRepository;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="healthExamRepository"></param>
    /// <param name="healthAnswerRepository"></param>
    /// <param name="healthEvaluationLogRepository"></param>
    /// <param name="healthUserExamAnswerRepository"></param>
    /// <param name="healthAnswerDeductRepository"></param>
    /// <param name="healthAnswerTargetRateRepository"></param>
    /// <param name="healthRiskResultRepository"></param>
    /// <param name="healthEvaluationRsultRepository"></param>
    public HealthAppService(RepositoryBase<HealthExam> healthExamRepository,
        RepositoryBase<HealthAnswer> healthAnswerRepository,
        RepositoryBase<HealthEvaluationLog> healthEvaluationLogRepository,
        RepositoryBase<HealthEvaluationExamAnswer> healthUserExamAnswerRepository,
        RepositoryBase<HealthAnswerDeduct> healthAnswerDeductRepository,
        RepositoryBase<HealthAnswerTargetRate> healthAnswerTargetRateRepository,
        RepositoryBase<HealthRiskResult> healthRiskResultRepository,
        RepositoryBase<HealthEvaluationRsult> healthEvaluationRsultRepository)
    {
        _healthAnswerRepository = healthAnswerRepository;
        _healthExamRepository = healthExamRepository;
        _healthEvaluationLogRepository = healthEvaluationLogRepository;
        _healthUserExamAnswerRepository = healthUserExamAnswerRepository;
        _healthAnswerDeductRepository = healthAnswerDeductRepository;
        _healthAnswerTargetRateRepository = healthAnswerTargetRateRepository;
        _healthRiskResultRepository = healthRiskResultRepository;
        _healthEvaluationRsultRepository = healthEvaluationRsultRepository;
    }

    /// <summary>
    /// 开始答题
    /// </summary>
    /// <param name="userId"></param>
    /// <param name="classify"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public async Task<AppResult<StartAnswerVo>> StartAnswer(long userId, int classify = 1)
    {
        var log = new HealthEvaluationLog()
        {
            Classify = classify,
            User = userId,
            Sex = String.Empty
        };
        var logId = await _healthEvaluationLogRepository.InsertReturnIdentityAsync(log);
        StartAnswerVo result = new StartAnswerVo() { Id = logId, Exams = new List<ExamAnswersVo>() };
        var exams = _healthExamRepository.Where(x => x.Classify == classify && x.IsDelete == false)
            .OrderBy(x => x.Sort)
            .ToList();

        var ids = exams.Select(x => x.Id).ToList();
        var answers = _healthAnswerRepository.Where(x => ids.Contains(x.Exam)).ToList();
        exams.ForEach(x =>
        {
            result.Exams.Add(new ExamAnswersVo
            {
                Exam = x.Adapt<HealthExamVo>(),
                Answers = answers.FindAll(c => c.Exam == x.Id)
                .OrderBy(c => c.Sort)
                .Select(c => new HealthAnswerVo
                {
                    Id = c.Id,
                    Content = c.Content,
                    Number = c.Number,
                }).ToList()
            });
        });
        return result;
    }

    /// <summary>
    /// 提交答案
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<long> UserAnswer(UserAnswerParaDto input)
    {
        var log = await _healthEvaluationLogRepository.GetByIdAsync(input.id);
        if (log == null || log.SubmitTime.HasValue)
            throw new Exception("参数错误或者重复提交!");

        List<HealthEvaluationExamAnswer> answers = new List<HealthEvaluationExamAnswer>();
        foreach (var item in input.answer)
        {
            var exaInfo = await _healthExamRepository.GetByIdAsync(item.eid);
            if (exaInfo == null)
                throw new Exception("参数错误!");
            switch (exaInfo.Type)
            {
                case 1:
                    if (string.IsNullOrEmpty(item.text))
                        throw new Exception("参数错误!");

                    answers.Add(new HealthEvaluationExamAnswer()
                    {
                        Id = IdGenerater.GetNextId(),
                        EvaluationLog = input.id,
                        Exam = item.eid,
                        Answer = item.text,
                        Time = DateTime.Now,
                    });
                    break;
                case 2:
                    if (item.aid <= 0)
                        throw new Exception("参数错误!");

                    answers.Add(new HealthEvaluationExamAnswer()
                    {
                        Id = IdGenerater.GetNextId(),
                        EvaluationLog = input.id,
                        Exam = item.eid,
                        Answer = item.aid.ToString(),
                        Time = DateTime.Now,
                    });
                    break;
                case 3:
                    if (item.aids == null || item.aids.Count == 0 || item.aids.Any(x => x <= 0))
                        throw new Exception("参数错误!");
                    answers.Add(new HealthEvaluationExamAnswer()
                    {
                        Id = IdGenerater.GetNextId(),
                        EvaluationLog = input.id,
                        Exam = item.eid,
                        Answer = String.Join(",", item.aids),
                        Time = DateTime.Now,
                    });
                    break;
                case 4:
                    break;
            }
        }
        log.SubmitTime = DateTime.Now;
        await _healthUserExamAnswerRepository.InsertRangeAsync(answers);
        await _healthEvaluationLogRepository.UpdateAsync(log);
        return input.id;
    }

    /// <summary>
    /// 生产报告
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public async Task<HealthReportVo> GetHealthUserExam(long id)
    {
        var totalPoints = 100;
        var log = await _healthEvaluationLogRepository.GetByIdAsync(id);
        if (log == null)
            throw new Exception("参数错误");
        if (!log.ReportTime.HasValue)
        {
            var examAnswer = await _healthUserExamAnswerRepository.Where(x => x.EvaluationLog == id).ToListAsync();
            var examIds = examAnswer.Select(x => x.Exam).ToList();
            var exams = await _healthExamRepository.Where(x => examIds.Contains(x.Id)).ToListAsync();
            var answers = await _healthAnswerRepository.Where(x => examIds.Contains(x.Exam)).ToListAsync();

            var userAnswer = (from ea in examAnswer
                              join e in exams on ea.Exam equals e.Id
                              select new
                              {
                                  Exam = e.Id,
                                  e.Type,
                                  e.BaseType,
                                  e.TallestDeduct,
                                  ea.Answer
                              }).ToList();

            //年龄
            var age = int.Parse(userAnswer.First(x => x.BaseType == 1).Answer);
            //性别
            var sex = answers.First(x => x.Id == long.Parse(userAnswer.First(c => c.BaseType == 2).Answer)).Content;
            //体重
            var weight = double.Parse(userAnswer.First(x => x.BaseType == 3).Answer);
            //身高
            var stature = double.Parse(userAnswer.First(x => x.BaseType == 4).Answer);
            //bmi
            // double bmi = weight / (stature / Double.Parse("100")) / (stature /  Double.Parse("100"));
            double bmi = double.Parse((weight / Math.Pow((stature / Double.Parse("100")), 2)).ToString("0.00"));
            if (bmi < 18.5)
                totalPoints -= 3; //过轻
            else if (bmi >= 24 && bmi <= 27.9)
                totalPoints -= 3; //超重
            else if (bmi >= 28)
                totalPoints -= 5; //肥胖
                                  //bmr
            double bmr = 0.00;
            switch (sex)
            {
                case "男":
                    bmr = (9.99 * weight) + (6.25 * stature) - (4.92 * age) + 5;
                    if (bmr < 1442.83)
                        totalPoints -= 5; //过低
                    else if (bmr > 1647.53)
                        totalPoints -= 5; //过高
                    break;
                case "女":
                    bmr = (9.99 * weight) + (6.25 * stature) - (4.92 * age) - 161;
                    if (bmr < 1154.37)
                        totalPoints -= 5; //过低
                    else if (bmr > 1415.19)
                        totalPoints -= 5; //过高
                    break;
            }

            var deducts = await _healthAnswerDeductRepository.Where(x => examIds.Contains(x.Exam)).ToListAsync();
            var rates = await _healthAnswerTargetRateRepository.Where(x => examIds.Contains(x.Exam)).ToListAsync();
            var riskResult = _healthRiskResultRepository.Where(x => 1 == 1)
                .ToList();
            var risks = riskResult
                .Select(x => new HealthRiskHitRatioDto()
            {
                ID = x.Id,
                Percentage = 0,
                RiskResult = x.Adapt<HealthRiskResultDto>(),
                Sex = sex,
                Age = age,
                Bmi = bmi,
                Bmr = bmr
            });

            //扣分
            foreach (var item in userAnswer)
            {
                if (item.BaseType.HasValue)
                    continue;
                //单选题直接扣分
                if (item.Type == 2)
                {
                    var deduct = deducts.FirstOrDefault(x => x.Exam == item.Exam && x.Answer == long.Parse(item.Answer));
                    if (deduct != null)
                        totalPoints -= deduct.Score;
                    var rate = rates.FirstOrDefault(x => x.Exam == item.Exam && x.Answer == long.Parse(item.Answer));
                    if (rate != null)
                        risks.First(x => x.ID == rate.RiskResult).Percentage += rate.Rate;
                }
                //多选题计算后扣分
                else if (item.Type == 3)
                {
                    var ids = item.Answer.ToLongArray(",");
                    var score = deducts.FindAll(x => x.Exam == item.Exam && ids.Contains(x.Answer)).Sum(x => x.Score);
                    if(!item.TallestDeduct.HasValue)
                        score = 0;
                    else if(score > item.TallestDeduct.Value)
                        score = item.TallestDeduct.Value;
                    totalPoints -= score;

                    var ur = rates.FindAll(x => x.Exam == item.Exam && ids.Contains(x.Answer)).ToList();
                    if (ur.Count > 0)
                    {
                        var riskIds = ur.Select(x => x.RiskResult).Distinct().ToList();
                        foreach (var risk in riskIds)
                        {
                            var maxRate = ur.FindAll(x => x.RiskResult == risk).OrderByDescending(x => x.MaxRate).First().MaxRate;
                            var rate = ur.Sum(x => x.Rate);
                            if (rate >= 100)
                                rate = 100;
                            else if (rate > maxRate)
                                rate = maxRate;
                            risks.First(x => x.ID == risk).Percentage += rate;
                        }
                    }

                }
            }

            var riskHitRatioViews = risks.OrderByDescending(x => x.Percentage)
                .Select(x =>
                    new HealthRiskVo
                    {
                        Id = x.ID,
                        Percentage = x.Percentage,
                        Type = x.RiskResult.Type,
                        Ico = x.RiskResult.Ico,
                        Describe = x.RiskResult.Describe,
                        Influencing = x.RiskResult.Influencing,
                        Riskhint = x.RiskResult.Riskhint,
                        Symptom = x.RiskResult.Symptom,
                        JYYY = x.RiskResult.JYYY,
                        JYSY = x.RiskResult.JYSY
                    });
            log.TotalPoints = totalPoints;
            log.Bmi = decimal.Parse(bmi.ToString());
            log.Bmr = decimal.Parse(bmr.ToString());
            log.Age = age;
            log.Sex = sex;
            log.ReportTime = DateTime.Now;

            await _healthEvaluationLogRepository.UpdateAsync(log);

            var healthEvaluationRsults = new List<HealthEvaluationRsult>();
            foreach(var item in riskHitRatioViews)
            {
                healthEvaluationRsults.Add(new HealthEvaluationRsult
                {
                    Id = IdGenerater.GetNextId(),
                    EvaluationLog = id,
                    RiskResult = item.Id,
                    Percentage = item.Percentage
                });
            }
            await _healthEvaluationRsultRepository.InsertRangeAsync(healthEvaluationRsults);
            riskHitRatioViews = riskHitRatioViews.Take(3);
            return new HealthReportVo
            {
                Points = totalPoints,
                BMI = decimal.Parse(bmi.ToString()),
                BMR = decimal.Parse(bmr.ToString()),
                Age = age,
                Sex = sex,
                Risks = riskHitRatioViews.ToList()
            };
        }
        else
        {
            var evaluationRsult = await _healthEvaluationRsultRepository.Where(x => x.EvaluationLog == id).OrderByDescending(x => x.Percentage).Take(3).ToListAsync();
            var ids = evaluationRsult.Select(x => x.RiskResult).ToList();
            var riskResult = await _healthRiskResultRepository.Where(x => ids.Contains(x.Id)).ToListAsync();

            var risks = (from r in riskResult
             join er in evaluationRsult on r.Id equals er.RiskResult
             select new HealthRiskVo
             {
                 Id = r.Id,
                 Ico = r.Ico,
                 Percentage = er.Percentage,
                 Describe = r.Describe,
                 Influencing = r.Influencing,
                 JYSY = r.JYSY,
                 JYYY = r.JYYY,
                 Riskhint = r.Riskhint,
                 Symptom = r.Symptom,
                 Type = r.Type
             }).ToList();

            return new HealthReportVo { 
                Points =  log.TotalPoints,
                Age = log.Age,
                BMI = log.Bmi,
                BMR = log.Bmr,
                Sex = log.Sex,
                Risks = risks,
            };
        }
    }
}